package com.attask.jenkins.scaffolding; import com.attask.jenkins.BuildWrapperUtils; import com.attask.jenkins.CollectionUtils; import com.attask.jenkins.templates.ImplementationBuildWrapper; import com.attask.jenkins.templates.TemplateBuildWrapper; import hudson.Extension; import hudson.model.AbstractProject; import hudson.model.BuildableItemWithBuildWrappers; import hudson.model.Descriptor; import hudson.model.Project; import hudson.model.RootAction; import hudson.model.TopLevelItem; import hudson.tasks.BuildWrapper; import hudson.util.DescribableList; import jenkins.model.Jenkins; import net.sf.json.JSON; import net.sf.json.JSONSerializer; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * User: Joel Johnson * Date: 6/19/12 * Time: 2:41 PM */ @Extension public class ScaffoldAction implements RootAction { private ScaffoldCache scaffoldCache = new ScaffoldCache(); public ScaffoldCache getAllScaffolding() { return scaffoldCache; } public void doFindVariablesForJob(StaplerRequest request, StaplerResponse response) throws IOException, ServletException { response.setContentType("application/json"); Map<String, Object> result = new HashMap<String, Object>(); if (!"get".equalsIgnoreCase(request.getMethod())) { response.setStatus(405); result.put("error", "GET expected, but was " + request.getMethod()); } else { String[] jobNames = request.getParameterValues("names"); Set<String> variables = new TreeSet<String>(); if (jobNames != null) { variables.addAll(getVariablesForJob(jobNames)); } result.put("result", variables); } ServletOutputStream outputStream = response.getOutputStream(); JSON json = JSONSerializer.toJSON(result); outputStream.print(json.toString()); outputStream.flush(); } public Map<String, String> getVariablesForImplementation(String jobName) { AbstractProject nearest = Project.findNearest(jobName); ImplementationBuildWrapper buildWrapper = BuildWrapperUtils.findBuildWrapper(ImplementationBuildWrapper.class, nearest); if(buildWrapper != null) { String variables = buildWrapper.getVariables(); return CollectionUtils.expandToMap(variables); } return null; } private Set<String> getVariablesForJob(String... jobNames) throws IOException { Set<String> variables = new TreeSet<String>(); for (String jobName : jobNames) { AbstractProject nearest = Project.findNearest(jobName); String configFile = nearest.getConfigFile().asString(); Pattern pattern = Pattern.compile("\\$\\$([\\w\\d_]+)\\b"); Matcher matcher = pattern.matcher(configFile); while (matcher.find()) { String variableName = matcher.group(1); //Group one doesn't include the $$ or the \\b variables.add(variableName); } } return variables; } public void doCreateScaffold(StaplerRequest request, StaplerResponse response) throws IOException, ServletException { String name = request.getParameter("name"); String[] jobNames = request.getParameterValues("jobNames"); String[] variableNames = request.getParameterValues("variables"); if (name != null && jobNames != null) { if (variableNames == null) { variableNames = new String[]{}; } Scaffold scaffold = new Scaffold(name, Arrays.asList(jobNames), Arrays.asList(variableNames)); scaffoldCache.put(scaffold); scaffold.save(); } String rootUrl = Jenkins.getInstance().getRootUrl() == null ? "/" : Jenkins.getInstance().getRootUrl(); response.sendRedirect(rootUrl + getUrlName()); } public void doDeleteScaffold(StaplerRequest request, StaplerResponse response) throws IOException, ServletException { String name = request.getParameter("name"); scaffoldCache.remove(name); Scaffold.delete(name); String rootUrl = Jenkins.getInstance().getRootUrl() == null ? "/" : Jenkins.getInstance().getRootUrl(); response.sendRedirect(rootUrl + getUrlName()); } public void doDeleteJobs(StaplerRequest request, StaplerResponse response) throws IOException, ServletException, InterruptedException { String suffix = request.getParameter("suffix"); String scaffoldName = request.getParameter("scaffoldName"); Scaffold scaffold = scaffoldCache.get(scaffoldName); List<String> jobs = scaffold.getChildJobs().get(suffix); for (String jobName : jobs) { AbstractProject job = Project.findNearest(jobName); job.delete(); } String rootUrl = Jenkins.getInstance().getRootUrl() == null ? "/" : Jenkins.getInstance().getRootUrl(); response.sendRedirect(rootUrl + getUrlName()); } public void doStandUpScaffold(StaplerRequest request, StaplerResponse response) throws IOException, ServletException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { if (!"post".equalsIgnoreCase(request.getMethod())) { response.setStatus(405); return; } String scaffoldName = request.getParameter("scaffoldName"); String jobNameAppend = request.getParameter("jobNameAppend"); Scaffold scaffold = findScaffoldByName(scaffoldName); List<String> variables = scaffold.getVariables(); Map<String, String> variableValues = new HashMap<String, String>(); for (String variable : variables) { String value = request.getParameter(variable); variableValues.put(variable, value); } List<String> jobNames = scaffold.getJobNames(); for (String jobName : jobNames) { AbstractProject jobToClone = Project.findNearest(jobName); String newName = jobName + jobNameAppend; cloneJob(jobToClone, newName, variableValues); scaffold.addChildJob(jobNameAppend, newName); } scaffold.save(); String rootUrl = Jenkins.getInstance().getRootUrl() == null ? "/" : Jenkins.getInstance().getRootUrl(); response.sendRedirect(rootUrl + getUrlName()); } private TopLevelItem cloneJob(AbstractProject jobToClone, String newName, Map<String, String> variableValues) throws IOException, ServletException { TopLevelItem result = null; TemplateBuildWrapper templateBuildWrapper = BuildWrapperUtils.findBuildWrapper(TemplateBuildWrapper.class, jobToClone); if (templateBuildWrapper != null) { if (jobToClone instanceof TopLevelItem) { @SuppressWarnings("RedundantCast") //Need to cast to get the generics to work properly Class<? extends TopLevelItem> jobClass = ((TopLevelItem) jobToClone).getClass(); TopLevelItem newJob = Jenkins.getInstance().createProject(jobClass, newName); if (newJob != null && newJob instanceof BuildableItemWithBuildWrappers) { // If the target job (jobToClone) is actually a template, let's implement it rather than just clone it. BuildableItemWithBuildWrappers buildable = (BuildableItemWithBuildWrappers) newJob; DescribableList<BuildWrapper, Descriptor<BuildWrapper>> buildWrappersList = buildable.getBuildWrappersList(); TemplateBuildWrapper toRemove = BuildWrapperUtils.findBuildWrapper(TemplateBuildWrapper.class, buildable); buildWrappersList.remove(toRemove); String variablesAsPropertiesFile = squashVariables(variableValues); ImplementationBuildWrapper implementationBuildWrapper = new ImplementationBuildWrapper(jobToClone.getName(), newJob.getName(), variablesAsPropertiesFile); buildWrappersList.add(implementationBuildWrapper); newJob.save(); result = newJob; implementationBuildWrapper.sync(); } } } if (result == null) { //clone result = (TopLevelItem) Jenkins.getInstance().copy(jobToClone, newName); } return result; } public Scaffold findScaffoldByName(String scaffoldName) { if (scaffoldName == null || scaffoldName.isEmpty()) { return null; } return scaffoldCache.get(scaffoldName); } public String squashVariables(Map<String, String> variables) { StringBuilder sb = new StringBuilder("# Generated By Scaffolding\n"); for (Map.Entry<String, String> var : variables.entrySet()) { sb.append(var.getKey()).append("=").append(var.getValue()).append("\n"); } return sb.toString(); } public String getIconFileName() { return "/plugin/Templating/blueprint.png"; } public String getDisplayName() { return "Scaffolding"; } public String getUrlName() { return "scaffolding"; } }